const Emitter = require('events')
const http = require('http')
const Stream = require('stream')

let context = require('./context')
let request = require('./request')
let response = require('./response')


/**
 * 封装函数入口
 */
module.exports = class Application extends Emitter {
    constructor() {
        super();
        // {}.__proto__ = context  改了只会改前面的,但是可以通过链拿到
        this.context = Object.create(context)
        this.request = Object.create(request)
        this.response = Object.create(response)
        this.middleWares = [] //中间件  存储中间的过程

    }

    /**
     * 开启服务 +监听端口
     */

    listen(...args) {
        const server = http.createServer(this.handleRequest.bind(this));
        server.listen(...args);

    }

    use(fn) { // compose [fn,fn,fn,fn]
        // this.fn = fn
        this.middleWares.push(fn) //将函数存储到数组中
    }
    handleRequest(req, res) {
        let ctx = this.createContext(req, res)
        ctx.statusCode(404)
        // this.fn(ctx)

        // 将数组组合成一个大的promise
        this.compose(this.middleWares, ctx).then(() => {
            //判断类型再响应

            let body = ctx.body

            if (body instanceof Stream) { //判断是不是流  处理文件

                //下载头
                ctx.set('Context-Type', 'application/x-javascript')
                body.pipe(res) //如果是文件直接导出

            } else if (typeof body === 'object') {
                // res.setHeader('')
                ctx.set('Context-Type', 'application/json;charset=utf-8')
                //处理json格式
                res.end(JSON.stringify(body))
            } else if (typeof body === 'string' || Buffer.isBuffer(body)) {


                res.end(body) //将最后的值返回即可,实际上返回的是ctx.response的
            } else {
                res.end("NOT FOUND")
            }
        },(err)=>{
            this.emit('error',err)
            ctx.statusCode(500)
            res.end = '出错了'
        }).catch(e => {
           
            this.emit('error', e)
        })


    }
    createContext(req, res) {
        /**
         * 源码里面拷贝多次
         */

        let ctx = Object.create(this.context)
        ctx.request = Object.create(this.request)
        ctx.response = Object.create(this.response)
        //将原生的请求响应也放在自己封装的request 和response
        //ctx 上面帮原生req是为了封装的request可以取到原生的值
        ctx.req = ctx.request.req = req
        ctx.res = ctx.response.res = res




        return ctx

    }
    compose(middleWares, ctx) {
        //防止next多次调用
        let i = -1;

        function dispatch(index) { //取出第一个中间件执行

            if (index <= i) return Promise.reject('next() call multiple times')

            try {

                //到头来
                if (index === middleWares.length) return Promise.resolve()
                i = index
                //执行第二个参数next ,依然返回promise   Promise.resolve 为了保证返回的是一个promise  
                //如果用户调用前面必须加await
                return Promise.resolve(middleWares[index](ctx, () => dispatch(index + 1)))


            } catch (error) {
                
                return Promise.reject(error)
            }
        }
        return dispatch(0)
    }

}